﻿Public Module Utilities
    ' | Arps Utilities Module                                               |
    ' | Common Utilities code and methods provider                          |
    ' | By KDERazorback                                                     |
    ' | Project Started: 24/04/2011                                         |
    ' +=====================================================================+

    ''' <summary>
    ''' Compares two arrays
    ''' </summary>
    ''' <param name="arrayA">First array to be compared</param>
    ''' <param name="arrayB">Second array to be compared</param>
    ''' <returns>A boolean value indicating if the two specified arrays are identical or not</returns>
    ''' <remarks></remarks>
    Public Function CompareArrays(ByVal arrayA() As Byte, ByVal arrayB() As Byte) As Boolean
        ' Check if both are empty
        If (arrayA Is Nothing) And (arrayB Is Nothing) Then Return True
        If arrayA Is Nothing Then Return False
        If arrayB Is Nothing Then Return False

        ' Check if they are the same size
        If Not arrayA.Length = arrayB.Length Then Return False

        ' Check every item
        Dim i As Integer
        For i = 0 To arrayA.Length - 1
            If Not arrayA(i) = arrayB(i) Then Return False
        Next

        Return True
    End Function

    ''' <summary>
    ''' Used to Store the default supported Package Signature
    ''' </summary>
    ''' <value></value>
    ''' <returns>An array of bytes that contains an HEX dump of the supported Signature</returns>
    ''' <remarks></remarks>
    Friend ReadOnly Property SupportedPackageSignature As Byte()
        Get
            Dim out() As Byte
            out = {&H5E, &H41, &H72, &H70, &H68, &H6F, &H6F, &H6E, &H2F, &H2F, &H41, &H72, &H70, &H73, &H0, &H0}
            Return out
        End Get
    End Property

    Friend Function _UnifyBooleanValue(ByVal flag As Boolean) As String
        ' Unified a boolean flag to an static String

        If flag = True Then Return "TRUE" Else Return "FALSE"
    End Function

    Public Enum PackageDescriptorFlags As Byte
        JumpDirectly = &H0
        SkipMd5Checking = &H1
        DecompressAsDeflate = &H2
        DecompressAsGZip = &H4
        Reserved1 = &H8
        Reserved2 = &H10
        Reserved3 = &H20
        Reserved4 = &H40
        DebugMode = &H80
    End Enum

    Public Structure LoadedShadowCollection
        Friend _filename As String
        ''' <summary>
        ''' Full filename of the Shadow
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Filename As String
            Get
                Return _filename
            End Get
        End Property
        Friend _protocolVersion As String
        ''' <summary>
        ''' Stores the version of the Protocol used to build the Shadow package in the form of "Mmbrx" / Major Minor Build Revision Extended
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property ProtocolVersion As String
            Get
                Return _protocolVersion
            End Get
        End Property
        Friend _debugMode As Boolean
        ''' <summary>
        ''' Specify if the Shadow was loaded in Debug Mode. If so, the Engine will not tolerate errors or Missing parameters inside the package
        ''' </summary>
        ''' <value></value>
        ''' <returns>True if the Shadow was loaded in Debug mode, false if not</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property DebugMode As Boolean
            Get
                Return _debugMode
            End Get
        End Property
        Friend _size As ULong
        ''' <summary>
        ''' Stores the size in Bytes of the Shadow file
        ''' </summary>
        ''' <value></value>
        ''' <returns>An Unsigned Long value that represents the size in bytes of the Shadow file</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Size As ULong
            Get
                Return _size
            End Get
        End Property
        Friend _shadowLayer As Integer
        ''' <summary>
        ''' Stores an integer value specifying on wich layer the Shadow was loaded
        ''' </summary>
        ''' <value></value>
        ''' <returns>Integer value ranging from 0 to 255</returns>
        ''' <remarks>A shadow with a layer of 0 will be the first Shadow to load and modify the Original Package structure, a Shadow with a layer of 1, will load after that loaded at layer 0, and can overwrite the modifications of layer 0 and the Original Package</remarks>
        Public ReadOnly Property ShadowLayer As Integer
            Get
                Return _shadowLayer
            End Get
        End Property
        Friend _storedFiles As List(Of PackagedFileInformationCollection)
        ''' <summary>
        ''' Stores a list of the files on a Shadow
        ''' </summary>
        ''' <value>Array of string, storing each filename</value>
        ''' <returns>Returns an array of the files stored on the current Shadow file</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property StoredFiles() As List(Of PackagedFileInformationCollection)
            Get
                If _storedFiles Is Nothing Then Return Nothing Else Return _storedFiles
            End Get
        End Property
        ''' <summary>
        ''' Stores a list of the files on a Shadow
        ''' </summary>
        ''' <param name="index"></param>
        ''' <value>Index of the specified shadow on the collection</value>
        ''' <returns>Returns an the shadow at the specified index</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property StoredFiles(ByVal index As Long) As PackagedFileInformationCollection
            Get
                Return _storedFiles(index)
            End Get
        End Property
    End Structure
    Public Structure PackageInformationCollection
        Friend _filename As String
        ''' <summary>
        ''' Stores the full filename of the current Arphoon package
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Filename As String
            Get
                Return _filename
            End Get
        End Property
        Friend _shadowCapable As Boolean
        ''' <summary>
        ''' Stores a value indicating if the currently loaded Arphoon package can support the Loading of Shadows
        ''' </summary>
        ''' <value></value>
        ''' <returns>True if the Package can support Shadows, False if not</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property ShadowCapable As Boolean
            Get
                Return _shadowCapable
            End Get
        End Property
        Friend _protocolVersion As String
        ''' <summary>
        ''' Stores the version of the Protocol used to build the Arphoon package in the form of "Mmbrx" / Major Minor Build Revision Extended
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property ProtocolVersion As String
            Get
                Return _protocolVersion
            End Get
        End Property
        Friend _debugMode As Boolean
        ''' <summary>
        ''' Specify if the Package was loaded in Debug Mode. If so, the Engine will not tolerate errors or Missing parameters inside it
        ''' </summary>
        ''' <value></value>
        ''' <returns>True if the Package was loaded in Debug mode, false if not</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property DebugMode As Boolean
            Get
                Return _debugMode
            End Get
        End Property
        Friend _size As ULong
        ''' <summary>
        ''' Stores the size in Bytes of the Arphoon file
        ''' </summary>
        ''' <value></value>
        ''' <returns>An Unsigned Long value that represents the size in bytes of the Arphoon file</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Size As ULong
            Get
                Return _size
            End Get
        End Property
        Friend _dataSize As ULong
        ''' <summary>
        ''' Stores the total size of the packed files in bytes
        ''' </summary>
        ''' <value></value>
        ''' <returns>An unsigned long that represents the total bytes stored inside the package</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property DataSize As ULong
            Get
                Return _dataSize
            End Get
        End Property
        Friend _loadedShadow As List(Of LoadedShadowCollection)
        ''' <summary>
        ''' Stores each loaded Shadow in the form of LoadedShadowCollection
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property LoadedShadow As List(Of LoadedShadowCollection)
            Get
                Return _loadedShadow
            End Get
        End Property
        ''' <summary>
        ''' Stores each loaded Shadow in the form of LoadedShadowCollection
        ''' </summary>
        ''' <param name="index"></param>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property LoadedShadow(ByVal index As Long) As LoadedShadowCollection
            Get
                Return _loadedShadow(index)
            End Get
        End Property
        ''' <summary>
        ''' Stores information about the Compiler used to generate the Package
        ''' </summary>
        ''' <remarks></remarks>
        Friend _compilerDetails As PackageCompilerDetails
        Public ReadOnly Property CompilerDetails As PackageCompilerDetails
            Get
                Return _compilerDetails
            End Get
        End Property
        Friend _name As String
        Public ReadOnly Property Name As String
            Get
                Return _name
            End Get
        End Property

        Friend Property MftHashOffset As ULong
        Friend Property MftUseHash As Boolean
        Friend Property MftJumpFlags As PackageDescriptorFlags
        Friend Property MftOffset As ULong
        Friend Property MftSize As ULong

        Friend Property DescriptorOffset As ULong
        Friend Property DescriptorSize As ULong

        Friend Property DataBlockOffset As ULong

    End Structure

    Public Structure PackagedFileInformationCollection
        Friend _filename As String
        ''' <summary>
        ''' Stores the Filename of the data file currently Loaded
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks>This only stores the File Name, excluding the Path of the File</remarks>
        Public ReadOnly Property Filename As String
            Get
                Return _filename
            End Get
        End Property
        Friend _directory As String
        ''' <summary>
        ''' Stores the directory on wich the File is stored
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks>This returns the Path only without the File name</remarks>
        Public ReadOnly Property Directory As String
            Get
                Return _directory
            End Get
        End Property
        Friend _extension As String
        ''' <summary>
        ''' Return the File Extension of the file loaded
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks>Only returns the Extension part of the Filename, without the Dot. If the file dont have extension, this field will be empty</remarks>
        Public ReadOnly Property Extension As String
            Get
                Return _extension
            End Get
        End Property
        Friend _fullName As String
        ''' <summary>
        ''' Returns the Full path of the File. Including Filename, Path, and Extension
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property FullName As String
            Get
                Return _fullName
            End Get
        End Property
        Friend _length As ULong
        ''' <summary>
        ''' Stores the size of the File in bytes
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Length As ULong
            Get
                Return _length
            End Get
        End Property
        Friend _extendedParameters() As String
        ''' <summary>
        ''' Stores an array of strings in the form of Script Combos that represent any Extended Parameter that may be explicitly specified by the Package
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property ExtendedParameters As String()
            Get
                Return _extendedParameters
            End Get
        End Property
        Friend _md5Hash As String
        ''' <summary>
        ''' Stores the MD5 Sum of the File loaded
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks>The MD5 is stored as a string of 16 two digits Hexadecimal values in Uppercase</remarks>
        Public ReadOnly Property Md5Hash As String
            Get
                Return _md5Hash
            End Get
        End Property
        Friend _packagePath As String
        Friend _packageOffset As ULong
        Friend _internalSize As ULong
        Friend _packageFlags As PackageDescriptorFlags
        Friend _cached As Boolean
        ''' <summary>
        ''' Stores a flag that specify if the current file has been Cached to disk in order to reduce Package I/O, and CPU time
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Cached As Boolean
            Get
                Return _cached
            End Get
        End Property
        Friend _cachedCopyPath As String
        Friend _validationPerformed As Boolean
        ''' <summary>
        ''' Returns a value indicating if the current file has checked against its MD5. Doesnt care if match or not
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property ValidationPerformed As Boolean
            Get
                Return _validationPerformed
            End Get
        End Property
        Friend _validated As Boolean
        ''' <summary>
        ''' Returns a value indicating if the current file has already been checked against its MD5 and is valid
        ''' </summary>
        ''' <value></value>
        ''' <returns>True if the file has been checked and is valid, false if isnt checked or isnt valid</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Validated As Boolean
            Get
                Return _validated
            End Get
        End Property
        ''' <summary>
        ''' Validates this Stream against its MD5 as stored inside the Arphoon Package
        ''' </summary>
        ''' <returns>True if the file is Valid, False if not</returns>
        ''' <remarks></remarks>
        Public Function Validate() As Boolean
            Try

                If _length = 0 Then
                    _validated = True
                    _validationPerformed = True
                    Return True
                End If

                Dim pfStream As PackagedFileStream
                pfStream = GetStream()
                _validationPerformed = True
                If pfStream.Validate Then
                    _validated = True
                    pfStream.Close()
                    Return True
                Else
                    _validated = False
                    pfStream.Close()
                    Return False
                End If
            Catch ex As Exception
                _validationPerformed = False
                _validated = False
                Return False
            End Try
        End Function
        ''' <summary>
        ''' Generates an Stream pointing to to the file stored inside the Package
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks> 
        Public Function GetStream() As PackagedFileStream
            Dim tmpStream As New PackagedFileStream

            Dim fCollection As FilenameCollection
            fCollection.Directory = _directory
            fCollection.Extension = _extension
            fCollection.Filename = _filename
            fCollection.FullFilename = _fullName
            tmpStream._init(_packagePath, _packageOffset, _internalSize, _packageFlags, ExtendedParameters, Md5Hash, fCollection)

            tmpStream._validated = _validated
            tmpStream._validationPerformed = _validationPerformed

            Return tmpStream
        End Function
    End Structure

    Public Enum PackageCompileArchitecture
        ' When adding new values beyond 9, be sure to patch the code used to execute the descriptor command "compiler_arch" in order to allow values beyond that point
        Undefined = 0
        UnixGeneric = 1
        WindowsGeneric = 2
        Win32 = 3
        Win64 = 4
        Unix32 = 5
        Unix64 = 6
        Virtualized32 = 7
        Virtualized64 = 8
        Mobile = 9
    End Enum

    Public Structure PackageCompilerDetails
        Friend _productName As String
        Friend _productVersion As Version
        Friend _productArchitecture As PackageCompileArchitecture
        Friend _productOwner As String
        Friend _productIdentifier As String
        Friend _compileTime As Date
        Friend _comments As String

        Public ReadOnly Property ProductName As String
            Get
                Return _productName
            End Get
        End Property
        Public ReadOnly Property ProductVersion As Version
            Get
                Return _productVersion
            End Get
        End Property
        Public ReadOnly Property ProductArchitecture As PackageCompileArchitecture
            Get
                Return _productArchitecture
            End Get
        End Property
        Public ReadOnly Property ProductOwner As String
            Get
                Return _productOwner
            End Get
        End Property
        Public ReadOnly Property ProductIdentifier As String
            Get
                Return _productIdentifier
            End Get
        End Property
        Public ReadOnly Property CompileTime As Date
            Get
                Return _compileTime
            End Get
        End Property
        Public ReadOnly Property Comments As String
            Get
                Return _comments
            End Get
        End Property
    End Structure

    Public Structure PackageContentsCollection
        Friend _package As PackageInformationCollection
        ''' <summary>
        ''' Stores information about the Package itself and its loaded Shadows
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Package As PackageInformationCollection
            Get
                Return _package
            End Get
        End Property
        ' Patch: Use Generics to store File and Directory items dinamically
        ' Applied 24/09/2012 to fix Slow mounting due to copy the entire array over and over
        ' on each file insertion using Redim Preserve.
        Friend _file As System.Collections.Generic.List(Of PackagedFileInformationCollection)
        Public ReadOnly Property File() As PackagedFileInformationCollection()
            Get
                Return _file.ToArray()
            End Get
        End property
        Public ReadOnly Property File(ByVal index As Integer) As PackagedFileInformationCollection
            Get
                Return _file(index)
            End Get
        End Property
    End Structure

    ''' <summary>
    ''' Converts an Address inside a 4 bytes Array in Candy Format, to its Unsigned Long format
    ''' </summary>
    ''' <param name="array">4 Bytes array that stores the Candy Address</param>
    ''' <returns>RAW Address in an Unsigned Long type</returns>
    ''' <remarks></remarks>
    Public Function GetUnsignedLongFromCandyArray(ByVal array() As Byte) As ULong
        ' A Candy Address is just an scrambled 4 Bytes unsigned long value
        ' Unsigned Longs are stored in 4 Bytes as follows:
        '   00    00    00    00 : RAW Data
        '   1     2     3     4  : Octect  
        ' And a Candy address is stored as follows:
        '   00    00    00    00 : RAW Data
        '   3     4     1     2  : Octect  
        ' So, the Candy conversion from an unsigned value 06 04 FA D9 is: FA D9 06 04

        If array Is Nothing Then Return 0
        If Not array.Length = 4 Then
            Err.Raise(vbObjectError + 513, , "Conversion of Candy Address to RAW was invalid")
            Return 0
        End If

        Dim rawValue As String = ""
        Dim i As Integer
        For i = 0 To 3
            Dim hVar As String = ""
            Select Case i + 1
                Case 1
                    hVar = Hex(array(2))
                Case 2
                    hVar = Hex(array(3))
                Case 3
                    hVar = Hex(array(0))
                Case 4
                    hVar = Hex(array(1))
            End Select
            If hVar.Length = 1 Then hVar = "0" & hVar
            rawValue &= hVar
        Next

        rawValue = "&h" & rawValue

        Try
            Return rawValue
        Catch ex As Exception
            Err.Raise(vbObjectError + 513, , "Conversion of Candy Address to RAW was invalid")
            Return 0
        End Try
    End Function
    ''' <summary>
    ''' Converts an Address inside an Unsigned Long to its 4 bytes Array in Candy Format representation
    ''' </summary>
    ''' <param name="value">Unsigned Long that will be converted to a Candy Value</param>
    ''' <returns>Candy value in a 4 Bytes Array</returns>
    ''' <remarks></remarks>
    Public Function GetCandyArrayFromUnsignedLong(ByVal value As ULong) As Byte()
        ' A Candy Address is just an scrambled 4 Bytes unsigned long value
        ' Unsigned Longs are stored in 4 Bytes as follows:
        '   00    00    00    00 : RAW Data
        '   1     2     3     4  : Octect  
        ' And a Candy address is stored as follows:
        '   00    00    00    00 : RAW Data
        '   3     4     1     2  : Octect  
        ' So, the Candy conversion from an unsigned value 06 04 FA D9 is: FA D9 06 04

        If value < 256 Then Return {Byte.Parse(0), Byte.Parse(value), Byte.Parse(0), Byte.Parse(0)}

        Dim octect1 As Byte = 0
        Dim octect2 As Byte = 0
        Dim octect3 As Byte = 0
        Dim octect4 As Byte = 0

        Dim hOctect1 As String = ""
        Dim hOctect2 As String = ""
        Dim hOctect3 As String = ""
        Dim hOctect4 As String = ""

        Dim hValue As String = Hex(value)
        If hValue.Length > 4 Then hValue = Right(hValue, 4)

        Do Until hValue.Length > 3
            hValue = "0" & hValue
        Loop

        hOctect1 = hValue.Substring(0, 2)
        hOctect2 = hValue.Substring(2, 2)
        hOctect3 = hValue.Substring(4, 2)
        hOctect4 = hValue.Substring(6, 2)

        octect1 = "&h" & hOctect3
        octect2 = "&h" & hOctect4
        octect3 = "&h" & hOctect1
        octect4 = "&h" & hOctect2

        Return {octect3, octect4, octect1, octect2}
    End Function

    Friend Function _CompareArrays(ByVal arrayA() As Byte, ByVal arrayB() As Byte) As Boolean
        ' Compares two arrays and returns True if they are Equal, false if not

        If (arrayA Is Nothing) And (arrayB Is Nothing) Then Return True
        If Not arrayA.Length = arrayB.Length Then Return False

        Dim index As Integer
        For index = 0 To arrayA.Length - 1
            If Not arrayA(index) = arrayB(index) Then Return False
        Next

        Return True
    End Function

    Friend Structure FilenameCollection
        Public Filename As String
        Public Extension As String
        Public Directory As String
        Public FullFilename As String
    End Structure

    ''' <summary>
    ''' Consolidates an splitted instruction to its string delimited variant at the specific indexes
    ''' </summary>
    ''' <param name="splittedInstruction">Array that stores the splitted instruction</param>
    ''' <param name="startIndex">Index from where to start creating the new string</param>
    ''' <param name="endIndex">Last index that will be included in the string</param>
    ''' <param name="separator">Delimiter string used to separate items on the output</param>
    ''' <returns>The consolidated and string delimited version of the specified instruction array</returns>
    ''' <remarks>This function converts this splitted instruction array {"a","b","c","d"} to "a$b$c$d" when using "$" as delimiter</remarks>
    Public Function ConsolidateParameters(ByVal splittedInstruction() As String, ByVal startIndex As Integer, ByVal endIndex As Integer, ByVal separator As String) As String
        If splittedInstruction Is Nothing Then Return Nothing
        If splittedInstruction.Length = 0 Then Return Nothing

        If startIndex < 0 Then startIndex = 0
        If endIndex >= splittedInstruction.Length Then endIndex = splittedInstruction.Length - 1

        Dim output As String = ""
        Dim i As Integer
        For i = startIndex To endIndex
            output &= splittedInstruction(i)
            If Not i = endIndex Then output &= separator
        Next

        Return output
    End Function
End Module
